`timescale 1ns / 1ps
/*
 Copyright 2020 Sean Xiao, jxzsxsp@qq.com
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
 
 http://www.apache.org/licenses/LICENSE-2.0
 
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */

module rv32I_exu #(
    parameter [31:0] TMR_BASEADDR  = 32'h0200_0000,
    parameter [31:0] PLIC_BASEADDR = 32'h0c00_0000,
    parameter [31:0] CPU_BASEADDR  = 32'h8000_0000,
    parameter [31:0] MEM_BASEADDR  = 32'h9000_0000,
    parameter [31:0] UART_BASEADDR = 32'he000_0000,
    parameter [31:0] GPIO_BASEADDR = 32'hf000_0000
)
(
    input  sys_clk,

    input  [ 31: 0 ] i_ir,  // The instruction register
    input  [ 31: 0 ] i_PC,  // The PC register along with
    input  i_EXE_vld,

    input  i_CPU_load_vld,
    input  [ 31: 0 ] i_CPU_load_data,

    output o_wb_need,
    output o_ls_need,
    output o_wb_rdy,
    output o_ls_rdy,

    output [ 31: 0 ] o_exe_PC,

// load program data
    output o_CPU_cs,
    output [ 31: 0 ] o_CPU_PC,     
    
    input  [ 31: 0 ] i_GPIO_dina,
    output [ 31: 0 ] o_GPIO_douta,
    output [ 31: 0 ] o_GPIO_ta,
    
    input  [ 31: 0 ] i_GPIO_dinb,
    output [ 31: 0 ] o_GPIO_doutb,
    output [ 31: 0 ] o_GPIO_tb,
    
    input  [ 31: 0 ] i_GPIO_dinc,
    output [ 31: 0 ] o_GPIO_doutc,
    output [ 31: 0 ] o_GPIO_tc,

    input  [ 31: 0 ] i_GPIO_dind,
    output [ 31: 0 ] o_GPIO_doutd,
    output [ 31: 0 ] o_GPIO_td,

    input            i_ext_irq,
    input            i_sft_irq,
    input            i_tmr_irq,

    output           o_meie,
    output           o_msie,
    output           o_mtie,
    output           o_glb_irq,
    
    input            i_irq_src,
    input            i_exp_src,
    output           o_mret,
    output [31:0]    o_irq_pc,
    output [31:0]    o_mepc,    
    
//==============================================================================
    output           txd_start,
    output [7:0]     txd_data,
    input            txd_done,
//==============================================================================
    output [31:0]    o_sft_int_v,
    output [31:0]    o_timer_l,
    output [31:0]    o_timer_h,
          
    input  [31:0]    i_timer_l,
    input  [31:0]    i_timer_h, 
          
    output [31:0]    o_tcmp_l,
    output [31:0]    o_tcmp_h,
  
    output [31:0]    o_tm_ctrl,
          
    output [1:0]     o_timer_valid,
        
//==============================================================================
    output           o_wr_dcsr_ena,
    output           o_wr_dpc_ena,
    output           o_wr_dscratch_en,
    output [31:0]    o_wr_csr_nxt,

    output [31:0]    o_dcause,
    input  [31:0]    i_dcsr_r,
    input  [31:0]    i_dpc_r,
    input  [31:0]    i_dscratch_r,
    
    input            i_dbg_mode,
    input            i_dbg_halt,
    input            i_dbg_step,
    input            i_dbg_ebreakm,
    input            i_dbg_stpcyl,

//==============================================================================
    input            i_cpu_reset,
    input            rst_n
    
);

//===============================================================================

// Instantiate the Regfile
wire [ 4: 0 ] rf_rs1_idx;
wire [ 4: 0 ] rf_rs2_idx;
wire [ 31: 0 ] rf_rs1_val;
wire [ 31: 0 ] rf_rs2_val;

wire rf_wb_rd_wen;
wire [ 31: 0 ] rf_wb_val;
wire [ 4: 0 ] rf_wb_rd_idx;

wire alu_wb_rd_wen;
wire [ 31: 0 ] alu_wb_data;
wire [ 4: 0 ] alu_wb_rd_idx;


//===============================================================================
// Instantiate the Decode
wire RV32I;
//wire           dec_rs1_en;
//wire           dec_rs2_en;
//wire           dec_rd_wen;

wire [ 4: 0 ] dec_rs1_idx;
wire [ 4: 0 ] dec_rs2_idx;
wire [ 4: 0 ] dec_rd_idx;



wire [ 8: 0 ] dec_opimm_instr;
wire [ 9: 0 ] dec_op_instr;
wire [ 5: 0 ] dec_branch_instr;
wire [ 4: 0 ] dec_load_instr;
wire [ 2: 0 ] dec_store_instr;

wire [ 5: 0 ] dec_csr_instr; 
wire [ 11: 0] dec_csr_addr;


wire          dec_ecall;
wire          dec_ebreak;
wire          dec_dret;
wire          dec_wfi;

wire [ 31: 0 ] J_PC;
wire [ 31: 0 ] B_PC;

wire [ 31: 0 ] dec_I_imm;  //I-type immediate
wire [ 31: 0 ] dec_S_imm;  //R-type immediate
wire [ 31: 0 ] dec_B_imm;  //S-type immediate
wire [ 31: 0 ] dec_J_imm;  //J-type immediate
wire [ 31: 0 ] dec_U_imm;  //U-type immediate
wire [ 4: 0 ]  dec_shamt;
wire [ 31: 0 ] dec_csr_imm;

assign rf_rs1_idx = dec_rs1_idx;
assign rf_rs2_idx = dec_rs2_idx;


//assign            rf_wb_ena=rf_wb_rd_wen;

wire J_PC_vld;
wire B_PC_vld;

regfile_I regfile_I_inst
(
    .sys_clk    ( sys_clk ),
    
    .i_EXE_vld  ( i_EXE_vld | i_CPU_load_vld),
    
    .i_rs1_idx  ( rf_rs1_idx ),
    .i_rs2_idx  ( rf_rs2_idx ),
    
    .o_rs1_val  ( rf_rs1_val ),
    .o_rs2_val  ( rf_rs2_val ),
    
    .o_wb_rdy   ( o_wb_rdy ),
    .i_wb_wen   ( rf_wb_rd_wen ),
    .i_wb_rd_idx( rf_wb_rd_idx ),
    .i_wb_val   ( rf_wb_val )
);

//===============================================================================
// decode opcode [6:0]
wire [ 15: 0 ] dec_instr_group;

wire OP_IMM   = dec_instr_group[ 0 ];
wire OP       = dec_instr_group[ 1 ];
wire LUI      = dec_instr_group[ 2 ];
wire AUIPC    = dec_instr_group[ 3 ];
wire JAL      = dec_instr_group[ 4 ];
wire JALR     = dec_instr_group[ 5 ];
wire BRANCH   = dec_instr_group[ 6 ];
wire LOAD     = dec_instr_group[ 7 ];
wire STORE    = dec_instr_group[ 8 ];

wire CSR      = dec_instr_group[ 9 ];
wire FENCE    = dec_instr_group[ 10 ];

instr_dec instr_dec_inst
(
    .sys_clk        ( sys_clk ),
    .i_instr        ( i_ir ),
    .RV32I          ( RV32I ),  // output riscv type : 16,32,64 etc

    .o_rs1_idx      ( dec_rs1_idx ),
    .o_rs2_idx      ( dec_rs2_idx ),
    .o_rd_idx       ( dec_rd_idx ),

    .o_instr_group  ( dec_instr_group ),

    .o_opimm_instr  ( dec_opimm_instr ),
    .o_op_instr     ( dec_op_instr ),
    .o_branch_instr ( dec_branch_instr ),
    .o_load_instr   ( dec_load_instr ),
    .o_store_instr  ( dec_store_instr ),
    .o_fence_instr  (),

    .o_csr_instr    ( dec_csr_instr ),
    .o_csr_addr     ( dec_csr_addr ),

    .o_I_imm        ( dec_I_imm ),
    .o_S_imm        ( dec_S_imm ),
    .o_B_imm        ( dec_B_imm ),
    .o_J_imm        ( dec_J_imm ),
    .o_U_imm        ( dec_U_imm ),
    .o_shamt        ( dec_shamt ),
    
    .o_csr_imm      ( dec_csr_imm ),
    .o_mret         ( o_mret ),
    .o_ecall        ( dec_ecall ),
    .o_ebreak       ( dec_ebreak ),
    .o_dret         ( dec_dret ),
    .o_wfi          ( dec_wfi )
    
);


//===============================================================================
wire [ 31: 0 ] alu_D_PC;

exu_alu exu_alu_u
(
    .sys_clk        ( sys_clk ),
    .rst_n          ( rst_n ),
// decode index destination regfile
    .i_rd_idx       ( dec_rd_idx ),
// regfile rs1 value
    .i_rs1_val      ( rf_rs1_val ),
// current PC    
    .i_PC           ( i_PC ),
// op_imm group
    .i_OP_IMM       ( OP_IMM ),
    .i_LOAD         ( LOAD ),
    .i_opimm_instr  ( dec_opimm_instr ),
    .i_I_imm        ( dec_I_imm ),
// op group
    .i_OP           ( OP ),
    .i_op_instr     ( dec_op_instr ),
    .i_rs2_val      ( rf_rs2_val ),
    
// lui and pc group    
    .i_LUI          ( LUI ),
    .i_AUIPC        ( AUIPC ),
    .i_U_imm        ( dec_U_imm ),
// jump group
    .i_JAL          ( JAL ),
    .i_JALR         ( JALR ),
    .i_J_imm        ( dec_J_imm ),
// store group
    .i_STORE        ( STORE ),
    .i_S_imm        ( dec_S_imm ),
//=====================================================
// load  address pc
    .o_D_PC         ( alu_D_PC ),
// jump address pc    
    .o_J_vld        ( J_PC_vld ),
    .o_J_PC         ( J_PC ),

    .o_rd_wen       ( alu_wb_rd_wen ),
    .o_wb_rd_idx    ( alu_wb_rd_idx ),
    .o_wb_data      ( alu_wb_data )

);

wire lsu_wb_rd_wen;
wire [ 4: 0 ] lsu_wb_rd_idx;
wire [ 31: 0 ] lsu_wb_data;


exu_LSU   #
(
    .TMR_BASEADDR   ( TMR_BASEADDR ),
    .PLIC_BASEADDR  ( PLIC_BASEADDR ),
    .CPU_BASEADDR   ( CPU_BASEADDR ),
    .MEM_BASEADDR   ( MEM_BASEADDR ),
    .UART_BASEADDR  ( UART_BASEADDR ),
    .GPIO_BASEADDR  ( GPIO_BASEADDR )
)
exu_LSU_u
(
    .sys_clk        ( sys_clk ),
//===============================================================================
    .i_EXE_vld      ( i_EXE_vld ),
    .i_D_PC         ( alu_D_PC ), //since load instruction is I-Type instruction, alu_add_res automaticly become D_PC
    .i_LOAD         ( LOAD ),

    .i_load_instr   ( dec_load_instr ),

    
    .i_STORE        ( STORE ),
    .i_store_instr  ( dec_store_instr ),
    .i_rd_idx       ( dec_rd_idx ),
    .i_rs2_val      ( rf_rs2_val ),
    
    .o_CPU_cs       ( o_CPU_cs ),
    .o_CPU_PC       ( o_CPU_PC ),
    .i_CPU_load_data( i_CPU_load_data ),
//===============================================================================
    .o_ls_need      ( o_ls_need ),
    .o_ls_rdy       ( o_ls_rdy ),

    .o_rd_wen       ( lsu_wb_rd_wen ),
    .o_wb_rd_idx    ( lsu_wb_rd_idx ),
    .o_wb_data      ( lsu_wb_data ),
//===============================================================================
    .i_GPIO_dina    ( i_GPIO_dina ),
    .o_GPIO_douta   ( o_GPIO_douta ),
    .o_GPIO_ta      ( o_GPIO_ta ),
    
    .i_GPIO_dinb    ( i_GPIO_dinb ),
    .o_GPIO_doutb   ( o_GPIO_doutb ),
    .o_GPIO_tb      ( o_GPIO_tb ),
    
    .i_GPIO_dinc    ( i_GPIO_dinc ),
    .o_GPIO_doutc   ( o_GPIO_doutc ),
    .o_GPIO_tc      ( o_GPIO_tc ),

    .i_GPIO_dind    ( i_GPIO_dind ),
    .o_GPIO_doutd   ( o_GPIO_doutd ),
    .o_GPIO_td      ( o_GPIO_td ),
//===============================================================================
    .txd_start      ( txd_start ),
    .txd_data       ( txd_data ),
    .txd_done       ( txd_done ),
//===============================================================================
    .o_sft_int_v    ( o_sft_int_v ),
    .i_timer_l      ( i_timer_l ),
    .i_timer_h      ( i_timer_h ),

    .o_timer_l      ( o_timer_l ),
    .o_timer_h      ( o_timer_h ),

    .o_tcmp_l       ( o_tcmp_l ),
    .o_tcmp_h       ( o_tcmp_h ),
    .o_tm_ctrl      ( o_tm_ctrl ),

    .o_timer_valid  ( o_timer_valid ),
//===============================================================================
    .i_cpu_reset    ( i_cpu_reset ),
    .rst_n          ( rst_n )
    
) ;


exu_BRANCH   exu_BRANCH_u
(
    .sys_clk        ( sys_clk ),
    .rst_n          ( rst_n ),
// ============================================    
    .i_EXE_vld      ( i_EXE_vld ),
    
    .i_rs1_val      ( rf_rs1_val ),
    .i_rs2_val      ( rf_rs2_val ),
    
    .i_BRANCH       ( BRANCH ),
    .i_branch_instr ( dec_branch_instr ),

    .i_PC           ( i_PC ),
    .i_B_imm        ( dec_B_imm ),
    
    .o_B_vld        ( B_PC_vld ),
    .o_PC           ( B_PC )
) ;

wire        csr_wb_rd_wen;
wire [4:0]  csr_wb_rd_idx;
wire [31:0] csr_wb_data;

 
exu_CSR   exu_CSR_U
(
    .sys_clk        ( sys_clk ),

    .i_ext_irq      ( i_ext_irq ),
    .i_sft_irq      ( i_sft_irq ),
    .i_tmr_irq      ( i_tmr_irq ),
    
    .i_irq_src      ( i_irq_src ),
    .i_exp_src      ( i_exp_src ),
    .i_exe_pc       ( o_exe_PC ),
    .i_ir           ( i_ir ),

    .o_irq_pc       ( o_irq_pc ), 
    .o_mepc         ( o_mepc ),

    .i_mret_ena     ( o_mret ),

    .o_wr_dcsr_ena  ( o_wr_dcsr_ena ),
    .o_wr_dpc_ena   ( o_wr_dpc_ena ),
    .o_wr_dscrh_ena ( o_wr_dscrh_ena ),
    .o_wr_csr_nxt   ( o_wr_csr_nxt ),

    .o_dcause       ( o_dcause ),
    .i_dcsr_r       ( i_dcsr_r ),
    .i_dpc_r        ( i_dpc_r ),
    .i_dscratch_r   ( i_dscratch_r ),

    .i_EXE_vld      ( i_EXE_vld ),
    .i_dbg_mode     ( i_dbg_mode ),
    .i_dbg_stpcyl   ( i_dbg_stpcyl ),

    .i_SYSTEM       ( CSR ),
    .i_rs1_val      ( rf_rs1_val ),
    .i_rd_idx       ( dec_rd_idx ),
    .i_csr_instr    ( dec_csr_instr ),
    .i_csr_addr     ( dec_csr_addr ),
    .i_csr_imm      ( dec_csr_imm ),

    .o_rd_wen       ( csr_wb_rd_wen ), 
    .o_wb_rd_idx    ( csr_wb_rd_idx ),
    .o_wb_data      ( csr_wb_data ),

    .o_meie         (o_meie),
    .o_msie         (o_msie),
    .o_mtie         (o_mtie),
    .o_glb_irq      (o_glb_irq),
    
    .rst_n          ( rst_n & (!i_cpu_reset))
); 


wire [31:0] bj_PC = BRANCH ? B_PC : J_PC;
wire  bj_PC_vld   = B_PC_vld | JAL | JALR;
assign o_exe_PC   = bj_PC_vld ? bj_PC : (i_PC + 4);

assign rf_wb_rd_wen = lsu_wb_rd_wen | alu_wb_rd_wen | csr_wb_rd_wen;
assign rf_wb_val    = ( { 32{ lsu_wb_rd_wen } } & lsu_wb_data ) | 
                      ( { 32{ alu_wb_rd_wen } } & alu_wb_data ) |
                      ( { 32{ csr_wb_rd_wen } } & csr_wb_data );


assign rf_wb_rd_idx = alu_wb_rd_wen ? alu_wb_rd_idx :                       
                                     (lsu_wb_rd_wen ? lsu_wb_rd_idx : csr_wb_rd_idx );

assign o_wb_need    = rf_wb_rd_wen;

endmodule
